package mvcframework.servlet;

import mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static jdk.nashorn.api.scripting.ScriptUtils.convert;

/**
 * @ClassName MYDispatcherServlet
 * @Description TODO
 * @Author liang
 * @Date 2018/8/27 10:19
 * @Version 1.0
 **/
public class MYDispatcherServlet extends HttpServlet {
    private Properties p = new Properties();//所有的配置信息都存入了properties中

    private List<String> classNames = new ArrayList<String>();//存放类名

    private Map<String,Object> ioc = new HashMap<String,Object>();
    //容器
    //private Map<String,Method> handler =new HashMap<String,Method>();
    //容器改造
    private List<MYHandler> handler = new ArrayList<MYHandler>();
    //初始化阶段

    /**
     * 1.加载配置文件Application
     * 2.根据配置文件,扫描所有的相关的类
     * 3.初始化所有的相关类的实例,并且将其放入到IOC 容器中(Map)
     * 4.实现自动依赖注入
     * 5.初始化HandlerMapping
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("============初始化============");
        //1.
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2.
        doScanner(p.getProperty("scanPackage"));
        //3.
        doInstance();
        //4.
        doAutoried();
        //5.
        initHandlerMapping();
    }

    /**
     * 改造前存的方法改造存入的方法
     */
    private void initHandlerMapping() {
        if(ioc.isEmpty()) return;
        for(Map.Entry<String,Object> entry:ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            //只加载Controller注解的
            if(!clazz.isAnnotationPresent(MYController.class)) continue;
            String baseUrl = "";
            if(clazz.isAnnotationPresent(MYRequestMapping.class)){
                MYRequestMapping requestMapping = clazz.getAnnotation(MYRequestMapping.class);
                baseUrl = requestMapping.value();

            }
            Method[] methods = clazz.getMethods();
            for (Method method:methods) {
                if(!method.isAnnotationPresent(MYRequestMapping.class)) continue;
                MYRequestMapping requestMapping = method.getAnnotation(MYRequestMapping.class);
                /**
                 * 改造前
                 * String url = baseUrl + requestMapping.value().replaceAll("/+", "/");
                 *                 handler.put(url,method);
                 */
                //改造后存入list中
                String regex = ("/" + baseUrl+requestMapping.value() ).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                handler.add(new MYHandler(pattern,entry.getValue(),method));
                System.out.println("Mapping:"+regex+","+method);
            }
        }
    }

    private void doAutoried() {
        if(ioc.isEmpty()) return;
        for (Map.Entry<String,Object> entry:ioc.entrySet()) {
            //获取所有字段
            //不管是private 还是 public 还是 protected 都要强制注入
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field:fields) {
                //只处理使用了注解的类
                if(!field.isAnnotationPresent(MYAutowired.class)) continue;
                MYAutowired autowired = field.getAnnotation(MYAutowired.class);
                String beanName = autowired.value();
                if("".equals(beanName)){
                    beanName = field.getType().getName();//默认使用类型名作为key
                }
                //要访问私有的或者保护的,要强制授权访问
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void doInstance() {
        if(classNames.isEmpty()) return;
        //如果不为空，用反射机制将刚刚扫描进来的所有classname初始化
        for(String className:classNames){
            try {
                Class<?> clazz = Class.forName(className);
                //进入Bean的实例化阶段,初始化IOC容器,只初始化有注解的类
                /**
                 *IOC容器规则
                 * 1.key默认用类名首字母小写
                 * 2.如果用户指定名字优先使用
                 * 3.如果是接口,可以用接口的类型作为key
                 */

                if(clazz.isAnnotationPresent(MYController.class)){
                    String beanName = lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());

                }else  if(clazz.isAnnotationPresent(MYService.class)){
                    MYService service = clazz.getAnnotation(MYService.class);
                    String beanName = service.value();
                    if("".equals(beanName.trim())){
                        //如果不是默认值,则使用用户自定义的名字
                        beanName = lowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName,instance);
                    //如果是接口，使用接口类型作为key
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> i:interfaces) {
                        String name = i.getName();
                        ioc.put(name,instance);
                    }
                }else {
                    continue;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
     }

    /**
     * 匹配url
     * @param req
     * @param resp
     */
     private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        MYHandler handler   =  getHandler(req);
        if(handler == null){
            resp.getWriter().write("没有找到");
        }
        //获取方法的参数列表
        Class<?>[] parameTypes = handler.method.getParameterTypes();
        //保存所有需要自动赋值的参数值
        Object [] paramValues = new Object[parameTypes.length];
        Map<String,String[]> parameterMaps = req.getParameterMap();
        for(Map.Entry<String,String[]> parameterMap:parameterMaps.entrySet()){
            String value = Arrays.toString(parameterMap.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "");
            //如果找到匹配的对象，则开始填充参数值
             if(!handler.parmamIndexMapping.containsKey(parameterMap.getKey())) continue;
            int index = handler.parmamIndexMapping.get(parameterMap.getKey());
            paramValues[index]  = value;
        }
        //设置方法中的request和response对象
         int reqIndex = handler.parmamIndexMapping.get(HttpServletRequest.class.getName());
         paramValues[reqIndex] = req;
         int respIndex = handler.parmamIndexMapping.get(HttpServletResponse.class.getName());
        paramValues[respIndex] = resp;
        handler.method.invoke(handler.controller,paramValues);
     }

    private MYHandler getHandler(HttpServletRequest req) {
        if(handler.isEmpty()) return null;
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for(MYHandler handler: handler){
            Matcher matcher = handler.pattern.matcher(url);
            if(!matcher.matches()) continue;
            return handler;
        }
        return null;
    }


    /***
     * 首字母转成小写
     * @param str
     * @return
     */
     private String lowerFirstCase(String str){
         char[] chars = str.toCharArray();
         chars[0] += 32;
         return String.valueOf(chars);
     }
    private void doScanner(String packageName) {
        //进行递归扫描
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File classDir= new File(url.getFile());
        for(File file : classDir.listFiles()){
            if(file.isDirectory()){
                doScanner(packageName+"."+file.getName());
            }else {
                String className = packageName+"."+file.getName().replace(".class","");
                classNames.add(className);
            }
        }
    }

    private void doLoadConfig(String location)  {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            p.load(is);
        } catch (IOException e) {
            try {
                if(is != null) is.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    //运行时

    /**
     * 6.等待请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    /*
    内部类，将请求的url，方法，参数  -> 实例化
     */
    private class MYHandler {
        protected Object controller;//保存方法独特的实例
        protected Method method;//保存映射的方法
        protected Pattern pattern;//
        protected Map<String,Integer> parmamIndexMapping;//参数顺序

        /**
         * 构造
         * @param pattern
         * @param controller
         * @param method
         */
        protected MYHandler(Pattern pattern,Object controller,Method method){
            this.controller = controller;
            this.method = method;
            this.pattern = pattern;
            parmamIndexMapping = new HashMap<>();
            putParamIndexMapping(method);
        }

        /**
         * 封装请求参数
         * @param method
         */
        private void putParamIndexMapping(Method method) {
            //提取方法中加了注释的参数
            Annotation[][] pa = method.getParameterAnnotations();
            for(int i = 0; i < pa.length ; i++){
                for (Annotation a:pa[i]) {
                    if(a instanceof MYRequestParam){
                        String paramName = ((MYRequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            parmamIndexMapping.put(paramName,i);
                        }
                    }
                }
            }

            //提取方法中的request和response参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            for(int i = 0; i < parameterTypes.length;i++){
                Class<?> type = parameterTypes[i];
                if(type == HttpServletRequest.class || type == HttpServletResponse.class){
                    parmamIndexMapping.put(type.getName(),i);
                }
            }
        }



    }
}
